package com.google.code.jgchart;

import org.apache.commons.codec.EncoderException;
import org.apache.commons.codec.net.URLCodec;

public class GChart {

	// private static final String CHART_COLOR_PARAM = "chco=";

	private static final String CHART_SIZE_PARAM = "chs=";

	private static final String CHART_TITLE_PARAM = "chtt=";

	public static final int CHART_MAX_SIZE = 300000;

	private static final String GCHART_SERVICE_URI = "http://chart.apis.google.com/chart";

	public static String serviceUri = GCHART_SERVICE_URI;

	private String urlCodec = "UTF-8";

	private ChartType type;

	private String title;

	private int width;

	private int height;

	private ChartData data;

	private ChartDataScaling scaling;

	private LinearStripe linearStripe;

	private Legend legend;

	private Label label;

	private Axis axis;

	private GridLine gridLine;

	public GChart(ChartType type, ChartData data, int width, int height) {
		setType(type);
		if (data == null) {
			throw new IllegalArgumentException("chart data is null.");
		}
		this.data = data;
		setSize(width, height);
	}

	public GChart setUrlCodec(String urlCodec) {
		this.urlCodec = urlCodec;
		return this;
	}

	public GChart setType(ChartType type) {
		if (type == null) {
			throw new IllegalArgumentException("chart type is null.");
		}
		this.type = type;
		return this;
	}

	public GChart setTitle(String title) {
		this.title = title;
		return this;
	}

	public GChart setSize(int width, int height) {
		if (width * height > CHART_MAX_SIZE) {
			throw new IllegalArgumentException("over size.");
		}
		this.width = width;
		this.height = height;
		return this;
	}

	public GChart setScaling(ChartDataScaling scaling) {
		this.scaling = scaling;
		return this;
	}

	public GChart setLinearStripe(LinearStripe linearStripe) {
		this.linearStripe = linearStripe;
		return this;
	}

	public GChart setLegend(Legend legend) {
		this.legend = legend;
		return this;
	}

	public GChart setLabel(Label label) {
		this.label = label;
		return this;
	}

	public GChart setAxis(Axis axis) {
		this.axis = axis;
		return this;
	}

	public GChart setGridLine(GridLine gridLine) {
		this.gridLine = gridLine;
		return this;
	}

	// =======================================================

	private void appendUrlQuery(StringBuilder sb, UrlQuery urlQuery, String split) {
		if (urlQuery != null) {
			String[] query = urlQuery.getQuery();
			for (int i = 0; i < query.length; i++) {
				sb.append(split).append(query[i]);
			}
		}
	}

	private String genQuery(boolean host, boolean html) {
		StringBuilder sb = new StringBuilder();
		String split = html ? "&amp;" : "&";
		if (host) {
			sb.append(serviceUri).append("?");
		}
		sb.append(CHART_SIZE_PARAM).append(width).append("x").append(height); // size
		appendUrlQuery(sb, type, split);
		appendUrlQuery(sb, data, split);
		appendUrlQuery(sb, scaling, split);
		appendUrlQuery(sb, linearStripe, split);
		appendUrlQuery(sb, legend, split);
		appendUrlQuery(sb, label, split);
		appendUrlQuery(sb, axis, split);
		appendUrlQuery(sb, gridLine, split);

		if (title != null) {
			try {
				String encTitle = new URLCodec(urlCodec).encode(title.replaceAll("\n",
						"|"));
				sb.append(split).append(CHART_TITLE_PARAM).append(encTitle);
			} catch (EncoderException e) {
				throw new QueryEncoderException("title(" + title + ") encode("
						+ urlCodec + ") error.", e);
			}
		}
		return sb.toString();
	}

	public String getQuery() {
		return genQuery(false, false);
	}

	public String getHtmlQuery() {
		return genQuery(false, true);
	}

	public String getUrl() {
		return genQuery(true, false);
	}

	public String getHtmlUrl() {
		return genQuery(true, true);
	}

	// getter setter =====================================

	public int getWidth() {
		return width;
	}

	public GChart setWidth(int width) {
		this.width = width;
		return this;
	}

	public int getHeight() {
		return height;
	}

	public GChart setHeight(int height) {
		this.height = height;
		return this;
	}

	public ChartData getData() {
		return data;
	}

	public String getUrlCodec() {
		return urlCodec;
	}

	public ChartType getType() {
		return type;
	}

	public String getTitle() {
		return title;
	}

	public ChartDataScaling getScaling() {
		return scaling;
	}

	public LinearStripe getLinearStripe() {
		return linearStripe;
	}

	public Legend getLegend() {
		return legend;
	}

	public Label getLabel() {
		return label;
	}

	public Axis getAxis() {
		return axis;
	}

	public GridLine getGridLine() {
		return gridLine;
	}

}
